package com.wyj.rpc.handler;

import com.alibaba.fastjson.JSONObject;
import com.wyj.rpc.anno.RpcService;
import com.wyj.rpc.common.RpcRequest;
import com.wyj.rpc.common.RpcResponse;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * TODO: netty服务端业务处理类
 *  1、解析并缓存ioc容器中的所有rpcService
 *  2、接收客户端请求
 *  3、解析请求参数
 *  4、对请求作出处理
 *  5、封装并返回请求响应
 *
 * @author wuyijie
 * @date 2021/12/26 11:56 上午
 */
@Component
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {

    private static final Map<String, Object> SERVICE_MAP = new ConcurrentHashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(RpcService.class);
        Set<Map.Entry<String, Object>> entries = beansWithAnnotation.entrySet();
        for (Map.Entry<String, Object> next : entries) {
            Object serviceBean = next.getValue();
            Class<?>[] interfaces = serviceBean.getClass().getInterfaces();
            if (interfaces.length > 0) {
                SERVICE_MAP.put(interfaces[0].getName(), serviceBean);
            }
        }
    }

    /**
     * TODO: 通道读就绪事件
     *
     * @author wuyijie
     * @date 11:47 上午
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println("【服务端接收到请求···】");
        //1.将接收到的参数字符串转化为具体参数对象
        RpcRequest rpcRequest = JSONObject.parseObject(s, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse(rpcRequest.getRequestId(), null, null);
        try {
            //    2.找到具体点servcie，对请求作出处理
            Object handler = this.handler(rpcRequest);
            rpcResponse.setData(handler);
        } catch (Exception e) {
            e.printStackTrace();
            rpcResponse.setErrorMsg(e.getMessage());
        } finally {
            //3.对请求作出响应
            channelHandlerContext.writeAndFlush(JSONObject.toJSONString(rpcResponse));
        }
    }


    public Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        Object result = null;
        String serviceName = rpcRequest.getServiceName();
        if (SERVICE_MAP.containsKey(serviceName)) {
            //得到serviceBean
            Object serviceBean = SERVICE_MAP.get(serviceName);
            String methodName = rpcRequest.getMethodName();
            //    通过cglib，进行方法调用
            FastClass fastClass = FastClass.create(Thread.currentThread().getContextClassLoader(), serviceBean.getClass());
            FastMethod method = fastClass.getMethod(methodName, rpcRequest.getParameterTypes());
            result = method.invoke(serviceBean, rpcRequest.getParameters());
        } else {
            throw new RuntimeException("服务端不存在改服务，serviceName：" + serviceName);
        }

        return result;
    }


}
